package com.j256.ormlite.hmos.apptools;


import com.j256.ormlite.dao.BaseDaoImpl;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.db.SqlException;
import com.j256.ormlite.logger.Logger;
import com.j256.ormlite.logger.LoggerFactory;
import com.j256.ormlite.utils.LogUtil;

import ohos.app.Context;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * OpenHelperManager
 *
 */
public class OpenHelperManager {
    private static final String HELPER_CLASS_RESOURCE_NAME = "open_helper_classname";
    private static Logger logger = LoggerFactory.getLogger(OpenHelperManager.class);

    private static Class<? extends OrmLiteRdbOpenHelper> helperClass = null;
    private static volatile OrmLiteRdbOpenHelper helper = null;
    private static boolean isClosed = false;
    private static int instanceCount = 0;

    /**
     * setOpenHelperClass
     *
     * @param openHelperClass OrmLiteRdbOpenHelper
     */
    public static synchronized void setOpenHelperClass(Class<? extends OrmLiteRdbOpenHelper> openHelperClass) {
        if (openHelperClass == null) {
            helperClass = null;
        } else {
            innerSetHelperClass(openHelperClass);
        }
    }

    public static synchronized void setHelper(OrmLiteRdbOpenHelper helper) {
        OpenHelperManager.helper = helper;
    }

    /**
     * exception.
     *
     * @param context Context
     * @param openHelperClass OpenHelper
     * @return loadHelper generics
     */
    public static synchronized <T extends
            OrmLiteRdbOpenHelper> T getHelper(Context context,Class<? extends OrmLiteRdbOpenHelper> openHelperClass) {
        if (openHelperClass == null) {
            throw new IllegalArgumentException("openHelperClass argument is null");
        }
        innerSetHelperClass(openHelperClass);
        return loadHelper(context, openHelperClass);
    }

    /**
     * exception.
     *
     * @param context Context
     * @param className String
     * @return loadHelper generics
     */
    @Deprecated
    public static synchronized OrmLiteRdbOpenHelper getHelper(Context context, String className) {
        if (helperClass == null) {
            if (context == null) {
                throw new IllegalArgumentException("context argument is null");
            }
            Context appContext = context.getApplicationContext();
            innerSetHelperClass(lookupHelperClass(appContext, context.getClass(), className));
        }
        return loadHelper(context, helperClass);
    }

    /**
     * releaseHelper().
     *
     */
    public static synchronized void releaseHelper() {
        instanceCount--;
        logger.trace("releasing helper {}, instance count = {}", helper, instanceCount);
        if (instanceCount <= 0) {
            if (helper != null) {
                logger.trace("zero instances, closing helper {}", helper);
                helper.close();
                helper = null;
                isClosed = true;
            }
            if (instanceCount < 0) {
                logger.error("too many calls to release helper, instance count = {}", instanceCount);
            }
        }
    }

    private static void innerSetHelperClass(Class<? extends OrmLiteRdbOpenHelper> openHelperClass) {
        // make sure if that there are not 2 helper classes in an application
        if (openHelperClass == null) {
            throw new IllegalStateException("Helper class was trying to be reset to null");
        } else if (helperClass == null) {
            helperClass = openHelperClass;
        } else if (helperClass != openHelperClass) {
            throw new IllegalStateException("Helper class was " + helperClass + " but is trying to be reset to "
                    + openHelperClass);
        }
    }

    private static <T extends
            OrmLiteRdbOpenHelper> T loadHelper(Context context, Class<? extends OrmLiteRdbOpenHelper> openHelperClass) {
        if (helper == null) {
            if (isClosed) {
                logger.info("helper was already closed and is being re-opened");
            }
            if (context == null) {
                throw new IllegalArgumentException("context argument is null");
            }
            Context appContext = context.getApplicationContext();
            helper = constructHelper(appContext, openHelperClass);
            logger.trace("zero instances, created helper {}", helper);
            BaseDaoImpl.clearAllInternalObjectCaches();
            DaoManager.clearDaoCache();
            instanceCount = 0;
        }
        instanceCount++;
        logger.trace("returning helper {}, instance count = {} ", helper, instanceCount);
        @SuppressWarnings("unchecked")
        T castHelper = (T) helper;
        return castHelper;
    }

    /**
     * Call the constructor on our helper class.
     * @param context current
     * @param openHelperClass  class
     * @return the constructor on our helper class.
     */
    private static OrmLiteRdbOpenHelper
    constructHelper(Context context, Class<? extends OrmLiteRdbOpenHelper> openHelperClass) {
        Constructor<?> constructor;
        try {
            constructor = openHelperClass.getConstructor(Context.class);
        } catch (SqlException | NoSuchMethodException e) {
            throw new IllegalStateException("Could not "
                    + "find public constructor that single"
                    + " (Context) argument for helper class " + openHelperClass, e);
        }
        try {
            LogUtil.info("contructor creating", "helper");
            return (OrmLiteRdbOpenHelper) constructor.newInstance(context);
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
            throw new IllegalStateException("Could not construct"
                    + "instance of helper class " + openHelperClass, e);
        }
    }

    /**
     * Class Generics
     * @param context current
     * @param componentClass class
     * @param className class name
     * @return castClass ,castOpenHelperClass
     */
    private static Class<? extends OrmLiteRdbOpenHelper>
    lookupHelperClass(Context context, Class<?> componentClass, String className) {
        if (!className.equals("")) {
            try {
                @SuppressWarnings("unchecked")
                Class<? extends OrmLiteRdbOpenHelper> castClass =
                        (Class<? extends OrmLiteRdbOpenHelper>) Class.forName(className);
                return castClass;
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException("Could not create helper instance for class " + className, e);
            }
        }

        for (Class<?> componentClassWalk = componentClass; componentClassWalk != null; componentClassWalk =
                componentClassWalk.getSuperclass()) {
            Type superType = componentClassWalk.getGenericSuperclass();
            if (superType == null || !(superType instanceof ParameterizedType)) {
                continue;
            }
            Type[] types = ((ParameterizedType) superType).getActualTypeArguments();
            if (types == null || types.length == 0) {
                continue;
            }
            for (Type type : types) {
                // defense
                if (!(type instanceof Class)) {
                    continue;
                }
                Class<?> clazz = (Class<?>) type;
                if (OrmLiteRdbOpenHelper.class.isAssignableFrom(clazz)) {
                    @SuppressWarnings("unchecked")
                    Class<? extends OrmLiteRdbOpenHelper> castOpenHelperClass =
                            (Class<? extends OrmLiteRdbOpenHelper>) clazz;
                    return castOpenHelperClass;
                }
            }
        }
        throw new IllegalStateException(
                "Could not find OpenHelperClass because none of the generic parameters of class " + componentClass
                        + " extends OrmLiteSqliteOpenHelper. You should use getHelper(Context, Class) instead.");
    }
}
